package com.example.demo.api;

import com.example.demo.game.*;
import com.example.demo.model.User;
import com.example.demo.model.UserMapper;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import javax.annotation.Resource;
import java.io.IOException;

/**
 * @program: gobang
 * @author: JCM
 * @create: 2023-03-11 14:11
 * @description:
 **/
@Component
public class GameAPI extends TextWebSocketHandler {
    public ObjectMapper objectMapper = new ObjectMapper();
    @Autowired
    private RoomManager roomManager;

    @Autowired
    private OnlineUserManager onlineUserManager;
    @Resource
    private UserMapper userMapper;
    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        GameReadyResponse resp = new GameReadyResponse();

        // 1. 先获取到用户的身份信息，（从 httpsession 中拿到当前用户对象）
        User user = (User) session.getAttributes().get("user");
        if (user == null) {
            resp.setOk(false);
            resp.setReason("用户尚未登录！");
            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(resp)));
            return;
        }
        // 2. 判定当前用户是否已经进入房间(使用房间管理器查询)
        Room room = roomManager.getRoomByUserId(user.getUserId());
        if (room == null) {
            // 如果为 null，当前没有找到对应的房间
            resp.setOk(false);
            resp.setMessage("用户尚未匹配到！");
            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(resp)));
            return;
        }
        // 3. 判定当前是不是多开
        // 通过 OnlineUserManager 管理
        if(onlineUserManager.getFromGameRoom(user.getUserId()) != null
            || onlineUserManager.getFromGameHall(user.getUserId()) != null) {
            // 如果一个账号一边在游戏大厅，一边在游戏房间，这种情况也是多开
            resp.setOk(true);
            resp.setMessage("repeatConnection");
            resp.setReason("禁止重复登录！");
            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(resp)));
        }

        // 4. 设置当前玩家上线
        onlineUserManager.enterGameRoom(user.getUserId(),session);
        // 5. 把两个玩家加入到游戏房间中
        // 当前这个逻辑是在 game_room.html 页面加载的时候进行的
        // 前面的创建房间/匹配过程，是在 game_hall.html 页面中进行的
        // 因此前面匹配到对手之后，需要经过页面跳转，来到 game_room.html 才算正式匹配成功
        // 执行到当前逻辑说明玩家已经页面跳转成功
        synchronized (room) {
            if(room.getUser1() == null) {
                // 第一个玩家尚未加入房间
                // 把当前连上 websocket 的玩家作为 user1，加入到房间中
                room.setUser1(user);
                // 先进入房间的玩家设置为先手方
                room.setWhiteUser(user.getUserId());
                System.out.println("玩家："+user.getUsername()+" 已经准备就绪！（作为玩家1）");
                return;
            }
            if (room.getUser2() == null) {
                // 进入这个逻辑说明 玩家1 已经加入房间，此时 user 作为 玩家2 加入房间
                room.setUser2(user);
                System.out.println("玩家："+user.getUsername()+" 已经准备就绪！（作为玩家2）");
                // 当两个玩家都加入成功之后，服务器给这两个玩家都返回一个 websocket 响应
                // 通知玩家1
                noticeGameReady(room,room.getUser1(),room.getUser2());
                //System.out.println("user1："+room.getUser1().getUserId());
                // 通知玩家2
                noticeGameReady(room,room.getUser2(),room.getUser1());
                //System.out.println("user2："+room.getUser2().getUserId());
                return;
            }
        }

        // 6. 如果还有玩家尝试连接同一个房间，提示报错
        // 这种情况理论上不存在
        resp.setOk(false);
        resp.setReason("当前房间已满，您不能加入房间！");

    }


    private void noticeGameReady(Room room, User thisUser, User thatUser) throws IOException {
        GameReadyResponse resp = new GameReadyResponse();
        resp.setMessage("gameReady");
        resp.setOk(true);
        resp.setReason("");
        resp.setRoomId(room.getRoomId());
        // 传递用户名，用户对战页面展示
        resp.setThisUserName(thisUser.getUsername());
        resp.setThatUserName(thatUser.getUsername());
        int thisCurScore = userMapper.selectByName(thisUser.getUsername()).getScore();
        int thatCurScore = userMapper.selectByName(thatUser.getUsername()).getScore();
        resp.setThisScore(thisCurScore);
        resp.setThatScore(thatCurScore);
        resp.setThisUserId(thisUser.getUserId());
        resp.setThatUserId(thatUser.getUserId());
        resp.setWhiteUser(room.getWhiteUser());
        // 把当前响应数据传回给玩家
        WebSocketSession webSocketSession = onlineUserManager.getFromGameRoom(thisUser.getUserId());
        webSocketSession.sendMessage(new TextMessage(objectMapper.writeValueAsString(resp)));
    }

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        // 1. 先从 session 里拿到当前用户的身份信息
        User user = (User) session.getAttributes().get("user");
        if (user == null) {
            System.out.println("[handleTextMessage] 当前玩家尚未登录！");
            return;
        }
        // 2. 根据玩家 id 获取房间对象
        Room room = roomManager.getRoomByUserId(user.getUserId());
        String messageContent = message.getPayload();

        // 3. 通过 room 来处理这次具体的请求
        // 如果 messageContent 中包含 message和userId 即可视为是下棋操作
        // 不包含的话，则是发送信息
        if(messageContent.contains("putChess")) {
            room.putChess(message.getPayload());
        } else {
            String newMessage = user.getUsername()+":"+messageContent;
            System.out.println("收到信息："+newMessage);
            room.updateMessage(message.getPayload());
        }

    }

    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        User user = (User) session.getAttributes().get("user");
        if (user == null) {
            // 此处简单处理，断开连接的时候不给客户端返回响应了
            return;
        }
        WebSocketSession exitSession = onlineUserManager.getFromGameRoom(user.getUserId());
        if (session == exitSession) {
            // 加上这个判定，目的是为了避免在多开的情况下，第二个用户退出连接动作
            onlineUserManager.exitGameRoom(user.getUserId());
            System.out.println("当前用户："+ user.getUsername()+"游戏房间连接异常！");
        }
        // 通知另一个玩家已经获胜
        noticeThatUserWin(user);
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        User user = (User) session.getAttributes().get("user");
        if (user == null) {
            // 此处简单处理，断开连接的时候不给客户端返回响应了
            return;
        }
        WebSocketSession exitSession = onlineUserManager.getFromGameRoom(user.getUserId());
        if (session == exitSession) {
            // 加上这个判定，目的是为了避免在多开的情况下，第二个用户退出连接动作
            onlineUserManager.exitGameRoom(user.getUserId());
            System.out.println("当前用户："+ user.getUsername()+"已经退出房间！");
        }
        // 通知另一个玩家已经获胜
        noticeThatUserWin(user);

    }

    private void noticeThatUserWin(User user) throws IOException {
        // 1. 根据当前玩家找到所属房间
        Room room = roomManager.getRoomByUserId(user.getUserId());
        if (room == null) {
            // 该房间已销毁
            System.out.println("该房间已释放，无需通知对方");
            return;
        }
        // 2. 根据房间找对手
        User thatUser = user == room.getUser1() ? room.getUser2() : room.getUser1();
        // 3. 找到对手的在线状态
        WebSocketSession webSocketSession = onlineUserManager.getFromGameRoom(thatUser.getUserId());
        if (webSocketSession == null) {
            // 对手也掉线了
            System.out.println("您的对手也掉线了，无需通知");
            return;
        }
        // 4. 构造一个响应来通知对手
        System.out.println("user: "+user.getUserId());
        System.out.println("thatUser:"+thatUser.getUserId());
        GameResponse resp = new GameResponse();
        resp.setMessage("putChess");
        resp.setWinner(thatUser.getUserId());
        resp.setUserId(thatUser.getUserId());
        System.out.println(resp.toString());
        webSocketSession.sendMessage(new TextMessage(objectMapper.writeValueAsString(resp)));

        // 5. 更新玩家分数信息
        int winUserId = thatUser.getUserId();
        int loseUserId = user.getUserId();
        userMapper.userWin(winUserId);
        userMapper.userLose(loseUserId);
        // 6. 释放房间对象
        roomManager.remove(room.getRoomId(),room.getUser1().getUserId(),room.getUser2().getUserId());

    }
}
